﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;

namespace Hypermarket_Serial_Port
{
    public enum STATE : byte
    {
        Authentication = 0x1F, LogOut = 0x15, TransactionVerify = 0x25, CheckOut = 0x3F, DeleteVerify = 0x45, 
        CancellationVerify = 0x5F, PriceTagInfo = 0x02, RequestPriceInfo = 0x65, RequestLastItemInfo = 0x75, 
        DeleteLastItemVerifyState = 0x7F, EndCommunciate = 0x04, ClientExistance = 0x04, CallManager = 0x8F 
    };  

    public enum MACHINE : byte
    {
        CashierRegister = 0x00, PriceTag = 0x80
    }
    class ProtocolManager
    {
        /*********************Protocol*****************************/
        private const byte PROTOCOL_START_BYTE = 0x7E;
        private const byte PROTOCOL_STOP_BYTE = 0x7F;
        private const byte PROTOCOL_ESCAPE_BYTE = 0x7D;
        private const byte PROTOCOL_XOR_ESCAPE_BYTE = 0x5D;
        private const byte PROTOCOL_XOR_START_BYTE = 0x5E;
        private const byte PROTOCOL_XOR_STOP_BYTE = 0x5F;

        private const byte END_COMMUNICATION_MSG = 0x21;
        private const byte EXISTANCE_MSG = 0x21;

        private const string PROTOCOL_VALID = "Y";
        private const string PROTOCOL_INVALID = "N";

        private const int RECEIVE_ID_INDEX = 1;
        private const int RECEIVE_TYPE_INDEX = 2;
        private const int RECEIVE_MSG_INDEX = 3;

        private const int SEND_ID_INDEX = 0;
        private const int SEND_TYPE_INDEX = 1;
        private const int SEND_VALID_INDEX = 2;
        private const int SEND_CHECKOUT_DEDUCT_PRICE_INDEX = 3;
        private const int SEND_CHECKOUT_TOTAL_PRICE_INDEX = 4;

        //login
        private const int RECEIVED_CASHIERID_INDEX = 3;
        private const int RECEIVED_PASSWORD_INDEX = 5;
        private const int CASHIER_BCD_SIZE = 2;

        //transaction
        private const int RECEIVED_BARCODE_INDEX = 3;
        private const int RECEIVED_QUANTITY_INDEX = 7;
        private const int SEND_NAME_INDEX = 3;
        private const int SEND_PRICE_INDEX = 4;
        private const int BARCODE_BCD_SIZE = 4;
        private const int QUANTITY_HEX_SIZE = 2;

        //delete
        private const int SEND_DEDUCET_PRICE_INDEX = 3;

        //
        private const int NAME_INDEX = 3;
        private const int PRICE_INDEX = 4;

        //price tag
        private const int PRICETAG_NAME_INDEX = 3;
        private const int PRICETAG_PRICE_INDEX = 4;

        private const int PROTOCOL_HEX = 16;
        private const int PROTOCOL_DEC = 10;

        private const byte MASK_MSB = 0x80;
        private const byte CLEAR_MSB = 0x7F;
        private const byte INT_TO_BYTE = 0x00FF;
        private const byte BYTE_SPACE = 0x20;
        private const byte BYTE_EMPTY = 0x00;

        private const int BYTE_SIZE = 8;
        private const int MIN_PROTOCOL_SIZE_DIFF = 3;
        private const int FIRST_POSITION = 0;
        private const int CANNOT_FIND = -1;
        private const int PRODUCT_NAME_SIZE = 16;
        private const int SOUND_INDEX_SIZE = 20;

        private const char DOT = '.';
        private const char CHAR_SPACE = ' ';
        private const string STR_SPACE = " ";
        private const string DICTINARY_FILE_NAME = "cmudict.0.7a";

        /********************End Protocol**************************/


        static private List<byte> SERVER_BROADCAST = new List<byte> { 0x7E, 0, 0x01, 0x3F, 0x7F };        //?:0x3F

        static private List<byte> SERVER_REPLY_VALID = new List<byte> { 0x7E, 0, 0, 0x59, 0x7F };      //Y:0x59
        static private List<byte> SERVER_REPLY_INVALID = new List<byte> { 0x7E, 0, 0, 0x4E, 0x7F };    //N;0x4E

        private static Dictionary<string, byte> mWordSoundIndex = new Dictionary<string, byte> 
        {{"AA",29},{"AE",30},{"AH",31},{"AO",32},{"AW",33},{"AY",34},{"B",35},{"CH",36},{"D",37},{"DH",38},{"EH",39},{"ER",40},{"EY",41},
        {"F",42},{"G",43},{"HH",44},{"IH",45},{"IY",46}, {"JH",47},{"K",48},{"L",49},{"M",50},{"N",51},{"NG",52},{"OW",53},{"OY",54},
        {"P",55},{"R",56},{"S",57},{"SH",58},{"T",59},{"TH",60},{"UH",61},{"UW",62},{"V",63},{"W",64},{"Y",65},{"Z",66},{"ZH",67}};

        private static Dictionary<string, List<string>> mWordDictionary = new Dictionary<string, List<string>>();

        private static ProtocolManager mInstance;

        private ProtocolManager()
        {
            try
            {
                LoadWordDictionary();
            }
            catch
            {
                throw;
            }
        }

        public static ProtocolManager getInstance()
        {
            if (mInstance == null)
            {
                mInstance = new ProtocolManager();
            }
            return mInstance;
        }

        private void LoadWordDictionary()
        {
            List<string> textList = new List<string>(System.IO.File.ReadAllLines(DICTINARY_FILE_NAME));
            foreach (string c in textList)
            {
                List<string> list = c.Split(CHAR_SPACE).ToList();
                list.Remove(string.Empty);
                list.Remove(STR_SPACE);
                string key = list.First();
                list.RemoveAt(0);
                for (int i = 0; i < list.Count(); i++)
                {
                    string t = list[i];
                    t = Regex.Replace(t, @"\d", string.Empty);
                    list[i] = t;
                }
                mWordDictionary.Add(key, list);
            }
        }

        private bool CheckStartAndEndByte(ref byte[] data)
        {
            try
            {
                List<byte> inputData = data.ToList();
                int startPos = inputData.IndexOf(PROTOCOL_START_BYTE);
                if (startPos == CANNOT_FIND)
                    return false;
                inputData = inputData.GetRange(startPos, inputData.Count() - startPos);
                startPos = FIRST_POSITION;
                int stopPos = inputData.IndexOf(PROTOCOL_STOP_BYTE);
                if (stopPos == CANNOT_FIND)
                    return false;
                if (stopPos <= startPos)
                    return false;
                if (stopPos - startPos < MIN_PROTOCOL_SIZE_DIFF)
                    return false;
                data = inputData.GetRange(startPos, stopPos - startPos + 1).ToArray();
                if (data.First() == PROTOCOL_START_BYTE && data.Last() == PROTOCOL_STOP_BYTE)
                {
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        public byte[] GenerateBroadcast(string id, MACHINE machineType)
        {
            try
            {
                List<byte> broadcast = SERVER_BROADCAST;
                byte idByte = Convert.ToByte(id, PROTOCOL_DEC);
                idByte |= (byte)machineType;
                broadcast[RECEIVE_ID_INDEX] = idByte;
                return ByteStuffingSend(broadcast);
            }
            catch
            {
                throw;
            }
        }

        public bool CheckMachine(ref byte[] input, string machineId, MACHINE machineType)
        {
            if (!CheckStartAndEndByte(ref input))
            {
                return false;
            }
            input = ByteStuffingReceive(input.ToList());
            byte newMachineId = input[RECEIVE_ID_INDEX];
            byte oldMachineId = Convert.ToByte(machineId);
            switch (machineType)
            {
                case MACHINE.CashierRegister:
                    if (newMachineId == oldMachineId)
                        return true;
                    break;
                case MACHINE.PriceTag:
                    if ((newMachineId & CLEAR_MSB) == oldMachineId)
                        return true;
                    break;
            }
            return false;
        }

        public bool CheckEndOfCommunication(byte[] input)
        {
            if (input[RECEIVE_TYPE_INDEX] == (byte)STATE.EndCommunciate && input[RECEIVE_MSG_INDEX] == END_COMMUNICATION_MSG)
                return true;
            else
                return false;
        }

        public bool CheckExistance(byte[] input)
        {
            if (input[RECEIVE_TYPE_INDEX] == (byte)STATE.PriceTagInfo && input[RECEIVE_MSG_INDEX] == EXISTANCE_MSG)
                return true;
            else
                return false;
        }

        public bool CheckCallManager(byte[] input)
        {
            if (input[RECEIVE_TYPE_INDEX] == (byte)STATE.CallManager)
            {
                return true;
            }
            return false;
        }

        public List<string> ConvertHexMessageToString(byte[] input)
        {
            try
            {
                byte type = input[RECEIVE_TYPE_INDEX];
                List<string> output = new List<string>();
                switch (type)
                {
                    case (byte)STATE.LogOut:
                    case (byte)STATE.CheckOut:
                    case (byte)STATE.CancellationVerify:
                    case (byte)STATE.RequestLastItemInfo:
                    case (byte)STATE.DeleteLastItemVerifyState:
                        output = GenerateInfo(input);
                        break;

                    case (byte)STATE.Authentication:
                        output = GenerateLoginInfo(input);
                        break;

                    //case (byte)STATE.LogOut:
                    //    output = GenerateLogoutInfo(input);
                    //    break;

                    case (byte)STATE.TransactionVerify:
                        output = GenerateTransactionInfo(input);
                        break;

                    //case (byte)STATE.CheckOut:
                    //    output = GenerateCheckOutInfo(input);
                    //    break;

                    case (byte)STATE.DeleteVerify:
                        output = GenerateDeleteInfo(input);
                        break;

                    //case (byte)STATE.CancellationVerify:
                    //    output = GenerateCancellInfo(input);
                    //    break;

                    case (byte)STATE.RequestPriceInfo:
                        output = GenerateRequestPriceInfo(input);
                        break;

                    case (byte)STATE.PriceTagInfo:
                        output = GeneratePriceTagInfo(input);
                        break;

                    //case (byte)STATE.RequestLastItemInfo:
                    //    output = GenerateRequestLastItemInfo(input);
                    //    break;

                    //case (byte)STATE.DeleteLastItemVerifyState:
                    //    output = GenerateDeleteLastItemInfo(input);
                    //    break;
                }
                return output;
            }
            catch
            {
                throw;
            }
        }

        public byte[] ConvertStringMessageToHex(List<string> input)
        {
            try
            {
                byte type = Convert.ToByte(input[SEND_TYPE_INDEX]);
                List<byte> output = new List<byte>();
                switch (type)
                {
                    case (byte)STATE.Authentication:
                    case (byte)STATE.LogOut:
                    case (byte)STATE.CancellationVerify:
                    case (byte)STATE.DeleteLastItemVerifyState:
                        output = GenerateReply(input);
                        break;

                    case (byte)STATE.TransactionVerify:
                        output = GenerateTransactionReply(input);
                        break;

                    case (byte)STATE.DeleteVerify:
                        output = GenerateDeleteReply(input);
                        break;

                    case (byte)STATE.RequestPriceInfo:
                        output = GenerateRequestPriceReply(input);
                        break;

                    case (byte)STATE.RequestLastItemInfo:
                        output = GenerateRequestLastItemReply(input);
                        break;

                    case (byte)STATE.CheckOut:
                        output = GenerateCheckOutReply(input);
                        break;

                    case (byte)STATE.PriceTagInfo:
                        output = GeneratePriceTagReply(input);
                        break;

                    default:
                        output = SERVER_REPLY_INVALID;
                        output[RECEIVE_ID_INDEX] = Convert.ToByte(input[RECEIVE_ID_INDEX]);
                        output[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[RECEIVE_TYPE_INDEX]);
                        break;
                }
                return ByteStuffingSend(output);
            }
            catch
            {
                throw;
            }
        }

        public byte[] ByteStuffingSend(List<byte> data)
        {
            List<byte> tempData = new List<byte>();
            tempData.Add(data.First());
            for (int i = 1; i < data.Count() - 1; i++)
            {
                if (data[i] == PROTOCOL_START_BYTE)
                {
                    tempData.Add(PROTOCOL_ESCAPE_BYTE);
                    tempData.Add(PROTOCOL_XOR_START_BYTE);
                }
                else if (data[i] == PROTOCOL_STOP_BYTE)
                {
                    tempData.Add(PROTOCOL_ESCAPE_BYTE);
                    tempData.Add(PROTOCOL_XOR_STOP_BYTE);
                }
                else if (data[i] == PROTOCOL_ESCAPE_BYTE)
                {
                    tempData.Add(PROTOCOL_ESCAPE_BYTE);
                    tempData.Add(PROTOCOL_XOR_ESCAPE_BYTE);
                }
                else
                {
                    tempData.Add(data[i]);
                }
            }
            tempData.Add(data.Last());
            return tempData.ToArray();
        }

        public byte[] ByteStuffingReceive(List<byte> data)
        {
            List<byte> tempData = new List<byte>();
            tempData.Add(data.First());
            data.RemoveAt(0);
            while (data.Count() > 1)
            {
                if (data.First() == PROTOCOL_ESCAPE_BYTE && data[1] == PROTOCOL_XOR_ESCAPE_BYTE)
                {
                    tempData.Add(PROTOCOL_ESCAPE_BYTE);
                    data.RemoveAt(0);
                    data.RemoveAt(0);
                }
                else if (data.First() == PROTOCOL_ESCAPE_BYTE && data[1] == PROTOCOL_XOR_START_BYTE)
                {
                    tempData.Add(PROTOCOL_START_BYTE);
                    data.RemoveAt(0);
                    data.RemoveAt(0);
                }
                else if (data.First() == PROTOCOL_ESCAPE_BYTE && data[1] == PROTOCOL_XOR_STOP_BYTE)
                {
                    tempData.Add(PROTOCOL_STOP_BYTE);
                    data.RemoveAt(0);
                    data.RemoveAt(0);
                }
                else
                {
                    tempData.Add(data.First());
                    data.RemoveAt(0);
                }
            }
            tempData.Add(data.Last());
            return tempData.ToArray();
        }

        #region Price Tag
        private List<string> GeneratePriceTagInfo(byte[] input)
        {
            try
            {
                List<string> info = new List<string>();
                info.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                info.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return info;
            }
            catch
            {
                throw;
            }
        }

        public List<byte> GeneratePriceTagReply(List<string> input)
        {
            List<byte> data = new List<byte>();
            try
            {
                data.Add(PROTOCOL_START_BYTE);
                data.Add(Convert.ToByte(Convert.ToByte(input[SEND_ID_INDEX]) | MASK_MSB));
                data.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                string name = input[PRICETAG_NAME_INDEX].Trim();
                AddProductNameByte(data, name);
                string[] price = input[PRICETAG_PRICE_INDEX].Split(DOT);
                AddProductPriceByte(data, price);
                data.Add(PROTOCOL_STOP_BYTE);
                return data;
            }
            catch
            {
                throw;
            }
        }
        #endregion

        #region Cashier Registor Receive
        //Logout, check out, cancell, request last item, delete last item
        private List<string> GenerateInfo(byte[] input)
        {
            try
            {
                List<string> info = new List<string>();
                info.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                info.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return info;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateLoginInfo(byte[] input)
        {
            try
            {
                //1:mechine id; 3:cashier id  4:password
                List<string> loginInfo = new List<string>();

                byte[] cashierid = new byte[CASHIER_BCD_SIZE];
                byte[] password = new byte[CASHIER_BCD_SIZE];
                Array.Copy(input, RECEIVED_CASHIERID_INDEX, cashierid, 0, CASHIER_BCD_SIZE);
                Array.Copy(input, RECEIVED_PASSWORD_INDEX, password, 0, CASHIER_BCD_SIZE);

                loginInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                loginInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                loginInfo.Add(BCDToIntString(cashierid));
                loginInfo.Add(BCDToIntString(password));
                return loginInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateLogoutInfo(byte[] input)
        {
            try
            {
                List<string> logoutInfo = new List<string>();
                logoutInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                logoutInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return logoutInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateTransactionInfo(byte[] input)
        {
            try
            {
                List<string> transactionInfo = new List<string>();
                transactionInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                transactionInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                AddBarcodeString(input, transactionInfo);
                AddQuantityString(input, transactionInfo);
                return transactionInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateCheckOutInfo(byte[] input)
        {
            try
            {
                List<string> logoutInfo = new List<string>();
                logoutInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                logoutInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return logoutInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateDeleteInfo(byte[] input)
        {
            try
            {
                List<string> deleteInfo = new List<string>();
                deleteInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                deleteInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                AddBarcodeString(input, deleteInfo);
                AddQuantityString(input, deleteInfo);
                return deleteInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateCancellInfo(byte[] input)
        {
            try
            {
                List<string> cancellInfo = new List<string>();
                cancellInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                cancellInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return cancellInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateRequestPriceInfo(byte[] input)
        {
            try
            {
                List<string> requestInfo = new List<string>();
                requestInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                requestInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                AddBarcodeString(input, requestInfo);
                return requestInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateRequestLastItemInfo(byte[] input)
        {
            try
            {
                List<string> requestLastItemInfo = new List<string>();
                requestLastItemInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                requestLastItemInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return requestLastItemInfo;
            }
            catch
            {
                throw;
            }
        }

        private List<string> GenerateDeleteLastItemInfo(byte[] input)
        {
            try
            {
                List<string> deleteLastItemInfo = new List<string>();
                deleteLastItemInfo.Add(Convert.ToString(input[RECEIVE_ID_INDEX] & CLEAR_MSB));
                deleteLastItemInfo.Add(Convert.ToString(input[RECEIVE_TYPE_INDEX]));
                return deleteLastItemInfo;
            }
            catch
            {
                throw;
            }
        }
        #endregion

        #region Cashier Registor reply
        //For Login, Logout, Check out, Cancell reply, Delete Last Item
        private List<byte> GenerateReply(List<string> input)
        {
            try
            {
                List<byte> validOutput;
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput = SERVER_REPLY_VALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }

        //Has Sound
        private List<byte> GenerateTransactionReply(List<string> input)
        {
            try
            {
                List<byte> validOutput = new List<byte>();
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput.Add(PROTOCOL_START_BYTE);
                        validOutput.Add(Convert.ToByte(input[SEND_ID_INDEX]));
                        validOutput.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                        validOutput.Add(Convert.ToByte(PROTOCOL_VALID.ToArray().First()));
                        string name = input[SEND_NAME_INDEX];
                        AddProductNameByte(validOutput, name);
                        string[] pirce = input[SEND_PRICE_INDEX].Split(DOT);
                        AddProductPriceByte(validOutput, pirce);
                        //AddWordSoundIndex(validOutput, name);
                        AddNumberSoundIndex(validOutput, input[PRICE_INDEX]);
                        validOutput.Add(PROTOCOL_STOP_BYTE);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }

        private List<byte> GenerateDeleteReply(List<string> input)
        {
            try
            {
                List<byte> validOutput = new List<byte>();
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput.Add(PROTOCOL_START_BYTE);
                        validOutput.Add(Convert.ToByte(input[SEND_ID_INDEX]));
                        validOutput.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                        validOutput.Add(Convert.ToByte(PROTOCOL_VALID.ToArray().First()));
                        string name = input[SEND_NAME_INDEX];
                        AddProductNameByte(validOutput, name);
                        string[] pirce = input[SEND_PRICE_INDEX].Split(DOT);
                        AddProductPriceByte(validOutput, pirce);
                        validOutput.Add(PROTOCOL_STOP_BYTE);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }

        //Has Sound
        private List<byte> GenerateRequestPriceReply(List<string> input)
        {
            try
            {
                List<byte> validOutput = new List<byte>();
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput.Add(PROTOCOL_START_BYTE);
                        validOutput.Add(Convert.ToByte(Convert.ToByte(input[SEND_ID_INDEX])));
                        validOutput.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                        validOutput.Add(Convert.ToByte(PROTOCOL_VALID.ToCharArray().First()));

                        string name = input[NAME_INDEX].Trim();
                        AddProductNameByte(validOutput, name);
                        string[] price = input[PRICE_INDEX].Split(DOT);
                        AddProductPriceByte(validOutput, price);
                        AddNumberSoundIndex(validOutput, input[PRICE_INDEX]);
                        validOutput.Add(PROTOCOL_STOP_BYTE);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }

        private List<byte> GenerateRequestLastItemReply(List<string> input)
        {
            try
            {
                List<byte> validOutput = new List<byte>();
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput.Add(PROTOCOL_START_BYTE);
                        validOutput.Add(Convert.ToByte(Convert.ToByte(input[SEND_ID_INDEX])));
                        validOutput.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                        validOutput.Add(Convert.ToByte(PROTOCOL_VALID.ToCharArray().First()));

                        string name = input[NAME_INDEX].Trim();
                        AddProductNameByte(validOutput, name);
                        string[] price = input[PRICE_INDEX].Split(DOT);
                        AddProductPriceByte(validOutput, price);
                        validOutput.Add(PROTOCOL_STOP_BYTE);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }

        //Has Sound
        private List<byte> GenerateCheckOutReply(List<string> input)
        {
            try
            {
                List<byte> validOutput = new List<byte>();
                switch (input[SEND_VALID_INDEX])
                {
                    case PROTOCOL_VALID:
                        validOutput.Add(PROTOCOL_START_BYTE);
                        validOutput.Add(Convert.ToByte(Convert.ToByte(input[SEND_ID_INDEX])));
                        validOutput.Add(Convert.ToByte(input[SEND_TYPE_INDEX]));
                        validOutput.Add(Convert.ToByte(PROTOCOL_VALID.ToCharArray().First()));
                        string[] price = input[SEND_CHECKOUT_DEDUCT_PRICE_INDEX].Split(DOT);
                        AddProductPriceByte(validOutput, price);
                        AddNumberSoundIndex(validOutput, input[SEND_CHECKOUT_TOTAL_PRICE_INDEX]);
                        validOutput.Add(PROTOCOL_STOP_BYTE);
                        break;

                    case PROTOCOL_INVALID:
                    default:
                        validOutput = SERVER_REPLY_INVALID;
                        validOutput[RECEIVE_ID_INDEX] = Convert.ToByte(input[SEND_ID_INDEX]);
                        validOutput[RECEIVE_TYPE_INDEX] = Convert.ToByte(input[SEND_TYPE_INDEX]);
                        break;
                }
                return validOutput;
            }
            catch
            {
                throw;
            }
        }
        #endregion

        private string BCDToIntString(byte[] data)
        {
            int result = 0;
            foreach (byte bcd in data)
            {
                result *= 100;
                result += bcd & 0x0f;
                result += (10 * ((bcd & 0xf0) >> 4));
            }
            return result.ToString();
        }

        private string HEXToIntString(byte[] data)
        {
            int result = 0;
            foreach (byte bcd in data)
            {
                result *= 256;
                result += Convert.ToInt32(bcd);
            }
            return result.ToString();
        }

        private static void AddProductNameByte(List<byte> data, string name)
        {
            int count = 0;
            foreach (char c in name)
            {
                data.Add(Convert.ToByte(c));
                count++;
                if (count == PRODUCT_NAME_SIZE)
                    break;
            }

            while (count < PRODUCT_NAME_SIZE)
            {
                data.Add(BYTE_SPACE);
                count++;
            }
        }

        private static void AddProductPriceByte(List<byte> data, string[] price)
        {
            data.Add(Convert.ToByte((int.Parse(price.First()) >> BYTE_SIZE) & INT_TO_BYTE));
            data.Add(Convert.ToByte(int.Parse(price.First()) & INT_TO_BYTE));
            string decimalPrice = price.Last().ToString().PadRight(2, '0');
            decimalPrice = decimalPrice.Substring(0, 2);
            data.Add(Convert.ToByte(decimalPrice));
        }

        private void AddBarcodeString(byte[] input, List<string> transactionInfo)
        {
            byte[] barcode = new byte[BARCODE_BCD_SIZE];
            Array.Copy(input, RECEIVED_BARCODE_INDEX, barcode, 0, BARCODE_BCD_SIZE);
            transactionInfo.Add(BCDToIntString(barcode));
        }

        private void AddQuantityString(byte[] input, List<string> transactionInfo)
        {
            byte[] quantity = new byte[QUANTITY_HEX_SIZE];
            Array.Copy(input, RECEIVED_QUANTITY_INDEX, quantity, 0, QUANTITY_HEX_SIZE);
            transactionInfo.Add(HEXToIntString(quantity));
        }

        /******************************* SOUND INDEX**************************************/
        private void AddNumberSoundIndex(List<byte> data, string input)
        {
            if (input.IndexOf(DOT) == -1)
                input += ".00";
            string[] prices = input.Split(DOT);
            Int32 integerPrice = Int32.Parse(prices.First());
            int decimalPrice = int.Parse(prices.Last());
            List<int> soundIndexList = new List<int>();
            SoundIndexForInteger(integerPrice, soundIndexList);
            SoundIndexForDecimal(decimalPrice, soundIndexList);
            List<byte> soundIndexArr = ConvertSoundIndexToHexArr(soundIndexList);
            data.AddRange(soundIndexArr);
        }

        private List<byte> ConvertSoundIndexToHexArr(List<int> input)
        {
            List<byte> indexList = new List<byte>();
            while (input.Count() < SOUND_INDEX_SIZE)
            {
                input.Add(0);
            }
            foreach(int i in input)
            {
                byte soundIndex = Convert.ToByte(i);
                indexList.Add(soundIndex);
            }
            //for (int i = 0; i < input.Count(); i++)
            //{
            //    int index = input[i];
            //    byte soundIndex = Convert.ToByte(index);
            //    indexList.Add(soundIndex);
            //}
            return indexList;
        }

        private void SoundIndexForInteger(Int32 integerPrice, List<int> soundIndex)
        {
            int count = 0;
            while (integerPrice != 0)
            {
                List<int> index = SoundIndexForThreeIntegerNumber(integerPrice % 1000);
                if (index.Count() != 0)
                {
                    if (count == 0)
                    {
                        index.Add(0);
                        index.Add(27);
                        index.Add(0);
                    }
                    else if (count == 1)
                    {
                        index.Add(0);
                        index.Add(25);
                        index.Add(0);
                    }
                    else
                    {
                        index.Add(0);
                        index.Add(26);
                        index.Add(0);
                    }
                    soundIndex.InsertRange(0, index);
                }
                integerPrice /= 1000;
                count++;
            }
        }

        private void SoundIndexForDecimal(int decimalPrice, List<int> soundIndex)
        {
            List<int> index = SoundIndexForTwoDecimalNumber(decimalPrice);
            if (index.Count() != 0)
            {
                index.Add(0);
                index.Add(28);
                index.Add(0);
                soundIndex.AddRange(index);
            }
        }
  
        private List<int> SoundIndexForThreeIntegerNumber(int input)
        {
            int handred = -1;
            int ten = -1;
            int single = -1;
            string price = input.ToString().PadLeft(3, '0');
            if (price[0] == '0')
            {
                handred = 0;
            }
            else
            {
                handred = int.Parse(price[0].ToString());
            }

            if (price[1] == '0')
            {
                ten = 0;
            }
            else if (price[1] == '1')
            {
                ten = int.Parse(price.Substring(1, 2));
                single = 0;
            }
            else
            {
                ten = int.Parse(price[1].ToString());
            }

            if (single != 0)
            {
                single = int.Parse(price[2].ToString());
            }

            List<int> sound = new List<int>();
            if (handred >= 1)
            {
                sound.Add(handred + 1);
                sound.Add(24);
            }

            if (ten >= 13)
            {
                sound.Add(ten + 1);
                sound.Add(21);
            }
            else if (ten >= 10 && ten <= 12)
            {
                sound.Add(ten + 1);
            }
            else if (ten >= 3)
            {
                sound.Add(ten + 11);
                sound.Add(22);
            }
            else if (ten == 2)
            {
                sound.Add(23);
            }

            if (single > 0)
            {
                sound.Add(single + 1);
            }

            return sound;
        }

        private List<int> SoundIndexForTwoDecimalNumber(int input)
        {
            int ten = -1;
            int single = -1;
            string price = input.ToString().PadRight(2, '0');
            price = price.Substring(0, 2);

            if (price[0] == '0')
            {
                ten = 0;
            }
            else if (price[0] == '1')
            {
                ten = int.Parse(price.Substring(0, 2));
                single = 0;
            }
            else
            {
                ten = int.Parse(price[0].ToString());
            }

            if (single != 0)
            {
                single = int.Parse(price[1].ToString());
            }

            List<int> sound = new List<int>();

            if (ten >= 13)
            {
                sound.Add(ten + 1);
                sound.Add(21);
            }
            else if (ten >= 10 && ten <= 12)
            {
                sound.Add(ten + 1);
            }
            else if (ten >= 3)
            {
                sound.Add(ten + 11);
                sound.Add(22);
            }
            else if (ten == 2)
            {
                sound.Add(23);
            }

            if (single > 0)
            {
                sound.Add(single + 1);
            }
            return sound;
        }

        private void AddWordSoundIndex(List<byte> data, string input)
        {
            List<byte> soundIndex = new List<byte>();
            try
            {
                List<string> wordPhonetic;
                string[] wordArr = input.Split(CHAR_SPACE);
                if (!mWordDictionary.TryGetValue(wordArr.First().ToUpper(), out wordPhonetic))
                {
                    InsertEmptySound(data);
                    return;
                }

                SoundIndexForWord(soundIndex, wordPhonetic);
                data.AddRange(soundIndex);
            }
            catch
            {
                InsertEmptySound(data);
            }
        }

        private static void InsertEmptySound(List<byte> data)
        {
            for (int i = 0; i < SOUND_INDEX_SIZE; i++)
            {
                data.Add(BYTE_EMPTY);
            }
        }

        private static void SoundIndexForWord(List<byte> data, List<string> wordPhonetic)
        {
            int count = 0;
            foreach (string s in wordPhonetic)
            {
                count++;
                byte index = mWordSoundIndex[s];
                data.Add(index);
            }

            while (count < SOUND_INDEX_SIZE)
            {
                data.Add(BYTE_EMPTY);
                count++;
            }
        }
    }
}